package com.taikang.base.cloud.client;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.springframework.web.multipart.MultipartFile;

import com.taikang.base.cloud.client.dto.StorageFileVO;
import com.taikang.base.cloud.constant.CloudStorageConstant;
import com.taikang.base.cloud.exception.CloudStorageException;
import com.taikang.base.cloud.security.CloudStorageAccessType;

/**
 * 云存储API
 * @creator     taikang
 * @create-time Jun 30, 2015   5:46:05 PM
 * @version 1.0
 */
public class CloudStorageClient {
	
	private static Log logger = LogFactory.getLog(CloudStorageClient.class);
	
	/** 验证存储用户账户信息 */
	private static final String VERIFY_USER_URL = "/verifyStorageUser.htm";
	/** 上传文件 */
	private static final String UPLOAD_FILE_URL = "/uploadStorageFile.htm";
	/** 删除文件 */
	private static final String DELETE_FILE_URL = "/deleteStorageFile.htm";
	/** 下载文件 */
	private static final String DOWNLOAD_FILE_URL = "/downloadStorageFile.htm";
	/** 下载文件超时时间 */
	private static final int DOWNLOAD_FILE_TIMEOUT = 30000;
	/** UTF-8 字符集 */
	private static final String CHARSET_UTF8 = "UTF-8";

	/**
	 * 上传文件
	 * 直接上传Spring MVC的文件对象，先把它保存到临时目录下，再请求上传
	 * @param uploadFile Spring MVC的文件
	 * @param storageAccountName 访问账号
	 * @param storageAccountKey 访问key
	 * @param cloudStorageAccessType 文件访问类型
	 * @return StorageFileVO 云存储文件VO对象
	 * @throws CloudStorageException
	 */
	public static StorageFileVO uploadMultipartFile(
			MultipartFile uploadFile,
			String storageDomain,
			String storageAccountName,
			String storageAccountKey,
			CloudStorageAccessType cloudStorageAccessType) throws CloudStorageException{
		
		//1.文件存储在临时目录上
		String fileName = uploadFile.getOriginalFilename();
	    //获取上传文件类型的扩展名,先得到.的位置，再截取从.的下一个位置到文件的最后，最后得到扩展名  
        String fileExt = fileName.substring(fileName.lastIndexOf("."),fileName.length()).toLowerCase();  
		String tmpDir = System.getProperty("java.io.tmpdir");
		String tmpFilePath = tmpDir + File.separator + uuid() + fileExt;
		File tmpFile = new File(tmpFilePath);
		
		try{
			uploadFile.transferTo(tmpFile);
		}catch(Exception ex){
			String msg = "存储临时文件发生异常:"+ex.getMessage();
			logger.error(msg,ex);
			throw new CloudStorageException("存储临时文件失败，请检查java.io.tmpdir变量是否设置");
		}
		
		StorageFileVO vo = null;
		try{
			vo = uploadFile(tmpFile, storageDomain,storageAccountName, storageAccountKey, cloudStorageAccessType);
		}finally{
			FileUtils.deleteQuietly(tmpFile);
		}
		
		return vo;
	}
	
	/**
	 * 上传文件
	 * @param uploadFile 上传的文件
	 * @param storageAccountName 访问账号
	 * @param storageAccountKey 访问key
	 * @param cloudStorageAccessType 文件访问类型
	 * @return StorageFileVO 云存储文件VO对象
	 * @throws CloudStorageException
	 */
	public static StorageFileVO uploadFile(
			File file,
			String storageDomain,
			String storageAccountName,
			String storageAccountKey,
			CloudStorageAccessType cloudStorageAccessType) throws CloudStorageException{
		
		//1.校验用户信息
		String uploadToken = verifyStorageAccount(storageDomain,storageAccountName,storageAccountKey);
		
		if(null == uploadToken){
			throw new CloudStorageException("用户身份不合法");
		}
		
		logger.debug("身份校验通过，开始上传文件...");
		
		//2.上传文件
		StorageFileVO vo = uploadFileToServer(storageDomain,uploadToken,file,cloudStorageAccessType);
		
		if(null == vo){
			throw new CloudStorageException("上传文件失败");
		}
		
		logger.debug("上传文件成功，返回的URL:" + vo.getUrl());
		
		return vo;
	}
	
	/**
	 * 删除文件
	 * @param storageDomain 存储域名
	 * @param storageAccountName 访问账号
	 * @param storageAccountKey 访问key
	 * @param url 文件URL地址
	 * @throws CloudStorageException
	 */
	public static void deleteFile(
			String storageDomain,
			String storageAccountName,
			String storageAccountKey,
			String url) throws CloudStorageException{
		//1.校验用户信息
		String deleteToken = verifyStorageAccount(storageDomain,storageAccountName,storageAccountKey);
		
		if(null == deleteToken){
			throw new CloudStorageException("用户身份不合法");
		}
		
		logger.debug("身份校验通过，开始删除文件...");
		
		//2.删除文件
		Map<String,String> paramMap = new HashMap<String,String>();
		paramMap.put(CloudStorageConstant.STORAGE_FILE_URL_PARAM, url);
		paramMap.put(CloudStorageConstant.STORAGE_ACCESS_TOKEN_PARAM, deleteToken);
		
		String deleteFileUrl = storageDomain + DELETE_FILE_URL;
		JSONObject result = postData(deleteFileUrl, paramMap);
		
		String status = result.get("status").toString();
		String msg = result.get("message").toString();
		
		if(!"success".equals(status)){
			//删除失败
			throw new CloudStorageException(msg);
		}
	}
	
	/**
	 * 下载文件
	 * @param storageDomain 存储域名
	 * @param storageAccountName 访问账号
	 * @param storageAccountKey 访问Key
	 * @param url 文件url
	 * @param destPath 存储的目标路径
	 * @throws CloudStorageException
	 */
	public static void downloadFile(
			String storageDomain,
			String storageAccountName,
			String storageAccountKey,
			String url,
			String destPath) throws CloudStorageException {
		
		//1.校验用户信息
		String downloadFileToken = verifyStorageAccount(storageDomain,storageAccountName,storageAccountKey);
		
		if(null == downloadFileToken){
			throw new CloudStorageException("用户身份不合法");
		}
		
		logger.debug("身份校验通过，开始下载文件...");
		
		//2.下载文件
		String downloadUrl = storageDomain + DOWNLOAD_FILE_URL;
		Map<String,String> paramMap = new HashMap<String,String>();
		paramMap.put(CloudStorageConstant.STORAGE_FILE_URL_PARAM, url);
		paramMap.put(CloudStorageConstant.STORAGE_ACCESS_TOKEN_PARAM, downloadFileToken);
		
		downloadFileFromServer(downloadUrl,paramMap,destPath);
	}

	/**
	 * 校验用户身份
	 * @param storageAccountName
	 * @param storageAccountKey
	 * @return
	 */
	private static String verifyStorageAccount(String storageDomain,String storageAccountName,String storageAccountKey) {
		//1.发送参数
		Map<String,String> paramMap = new HashMap<String,String>();
		paramMap.put(CloudStorageConstant.STORAGE_ACCOUNT_NAME_PARAM, storageAccountName);
		paramMap.put(CloudStorageConstant.STORAGE_ACCOUNT_KEY_PARAM, storageAccountKey);
		
		String verifyUserUrl = storageDomain + VERIFY_USER_URL;
		JSONObject result = postData(verifyUserUrl, paramMap);
		
		if(null == result){
			return null;
		}
		//2.解析数据
		String status = result.get("status").toString();
		String msg = result.get("message").toString();
		if(!"success".equals(status)){
			logger.error("验证用户信息不通过，服务器端返回消息：" + msg);
			return null;
		}
		
		//3.获取服务器端针对本次请求的token值
		String token = result.get("token").toString();
		return token;
	}

	/**
	 * 上传文件
	 * @param uploadToken
	 * @param uploadFile
	 * @param cloudStorageAccessType
	 * @return
	 */
	private static StorageFileVO uploadFileToServer(String storageDomain,String uploadToken,File file,CloudStorageAccessType cloudStorageAccessType) {
		//1.发送请求
		Map<String,String> otherParamMap = new HashMap<String,String>();
		otherParamMap.put(CloudStorageConstant.STORAGE_ACCESS_TOKEN_PARAM, uploadToken);
		otherParamMap.put(CloudStorageConstant.STORAGE_UPLOAD_ACCESS_TYPE_PARAM, cloudStorageAccessType.toString());
		
		String uploadFileUrl = storageDomain + UPLOAD_FILE_URL;
		JSONObject result = postFile(uploadFileUrl,file,CloudStorageConstant.STORAGE_UPLOAD_FILE_PARAM, otherParamMap);
		
		if(null == result){
			return null;
		}
		
		//2.解析响应数据
		String status = result.get("status").toString();
		String msg = result.get("message").toString();
		if(!"success".equals(status)){
			logger.debug("上传文件失败，服务器端返回消息：" + msg);
			return null;
		}
		
		//3.获取服务器端针对本次请求的token值
		String fileName = result.get("fileName").toString();
		String url = result.get("url").toString();
		
		StorageFileVO vo = new StorageFileVO();
		vo.setFileName(fileName);
		vo.setUrl(url);
		
		return vo;
	}
	
	/**
	 * 发送POST请求参数
	 * @param url
	 * @param paramMap
	 * @return
	 * @throws Exception
	 */
	private static JSONObject postData(String url,Map<String,String> paramMap){
		CloseableHttpClient httpClient = HttpClients.createDefault();
		CloseableHttpResponse response = null;
		String result = null;
		try {
			// 构造请求
			HttpPost httpPost = new HttpPost(url);
			// 封装参数
			List<BasicNameValuePair> nvps = new ArrayList<BasicNameValuePair>();
			if(null != paramMap){
				for(Entry<String,String> item : paramMap.entrySet()){
					nvps.add(new BasicNameValuePair(item.getKey(),item.getValue()));
				}
			}
			httpPost.setEntity(new UrlEncodedFormEntity(nvps,"utf-8"));
			// 发起请求
			response = httpClient.execute(httpPost);
			// 获取响应数据
			result = EntityUtils.toString(response.getEntity(),Charset.forName("UTF-8"));
			logger.debug("接收到的POST响应内容：" + result);
		} catch (Exception ex) {
			String msg = "POST请求发送失败:" + ex.getMessage();
			logger.error(msg, ex);
			return null;
		} finally {
			try {
				response.close();
				httpClient.close();
			} catch (Exception e) {}
		}
		
		//2.解析响应数据
		try{
			//解析JSON
			JSONParser parser = new JSONParser();
			JSONObject jsonResult = (JSONObject) parser.parse(result);
			
			return jsonResult;
		}catch(Exception ex){
			String msg = "解析JSON数据发生异常：" + ex.getMessage();
			logger.error(msg,ex);
			return null;
		}
		
	}

	/**
	 * 提交文件
	 * @param url
	 * @param in
	 * @param fileParam
	 * @param otherParamMap
	 * @return
	 * @throws Exception
	 */
	private static JSONObject postFile(String url,File file,String fileParam,Map<String,String> otherParamMap){
		CloseableHttpClient httpClient = HttpClients.createDefault();
		CloseableHttpResponse response = null;
		String result = null;
		try {
			// 构造请求
			HttpPost httpPost = new HttpPost(url);
			
			//封装文件流
			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
			builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
			builder.setCharset(Charset.forName(CHARSET_UTF8));
			builder.addPart(fileParam, new FileBody(file));
			
			// 封装参数
			if(null != otherParamMap){
				for(Entry<String,String> item : otherParamMap.entrySet()){
					builder.addTextBody(item.getKey(), item.getValue());
				}
			}
			
			HttpEntity httpEntity = builder.build();
			httpPost.setEntity(httpEntity);
			// 发起请求
			response = httpClient.execute(httpPost);
			// 获取响应数据
			result = EntityUtils.toString(response.getEntity(),Charset.forName("UTF-8"));
			logger.debug("接收到的POST响应内容：" + result);
		} catch (Exception ex) {
			String msg = "POST请求发送失败:" + ex.getMessage();
			logger.error(msg, ex);
			return null;
		} finally {
			try {
				response.close();
				httpClient.close();
			} catch (Exception e) {}
		}
		
		if(StringUtils.isBlank(result)){
			return null;
		}
		
		//2.解析响应数据
		try{
			//解析JSON
			JSONParser parser = new JSONParser();
			JSONObject jsonResult = (JSONObject) parser.parse(result);
			
			return jsonResult;
		}catch(Exception ex){
			String msg = "解析JSON数据发生异常：" + ex.getMessage();
			logger.error(msg,ex);
			return null;
		}
	}
	
	/**
	 * 下载数据
	 * @param url
	 * @param paramMap
	 * @return
	 * @throws Exception
	 */
	private static void downloadFileFromServer(String url,Map<String,String> paramMap,String destPath){
		CloseableHttpClient httpClient = HttpClients.createDefault(); 
		OutputStream out = null;
		try {
			// 构造请求
			HttpPost httpPost = new HttpPost(url);
			// 封装参数
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			if(null != paramMap){
				for(Entry<String,String> item : paramMap.entrySet()){
					nvps.add(new BasicNameValuePair(item.getKey(),item.getValue()));
				}
			}
			UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nvps,"utf-8");
			httpPost.setEntity(entity);
			//设置请求超时
			RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(DOWNLOAD_FILE_TIMEOUT).setConnectTimeout(DOWNLOAD_FILE_TIMEOUT).build();//设置请求和传输超时时间
			httpPost.setConfig(requestConfig);
			//执行请求
			CloseableHttpResponse response = httpClient.execute(httpPost);  
			out = new BufferedOutputStream(new FileOutputStream(destPath),	2048000);
            byte[] buffer = EntityUtils.toByteArray(response.getEntity());
            out.write(buffer, 0, buffer.length);
			out.flush();
			
		} catch (Exception ex) {
			String msg = "下载文件失败:" + ex.getMessage();
			logger.error(msg, ex);
		} finally {
			IOUtils.closeQuietly(out);
			try{
				httpClient.close();
			}catch(Exception e){e.printStackTrace();}
		}
		
	}

	/**
	 * 生成uuid 去除"-"
	 * @return
	 */
	 private static String uuid(){
       String uuid=java.util.UUID.randomUUID().toString().toLowerCase().replaceAll("-", "");
       return uuid;
	 }
}
